﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml.Serialization;
using vJine.Core;
using vJine.Core.IO.Bin;
using vJine.Core.IO.Xml;
using vJine.Lua.GUI.BLL;
using vJine.Lua.GUI.UI;

namespace vJine.Lua.GUI.BML {
    #region

    public class TestCatalog {
        public string name { get; set; }

        public TestCase current { get; set; }
        public List<TestCase> Cases { get; set; }

        static string base_dir = "../TC";
        public static List<TestCatalog> load_cases() {
            string[] D = null;
            try {
                if (Directory.Exists(base_dir)) {
                    D = Directory.GetDirectories(base_dir);
                } else {
                    D = new string[] { };
                }
            } catch {
            }

            List<TestCatalog> catalogs = new List<TestCatalog>();

            for (int i = 0, len = D.Length; i < len; i++) {
                string cat_dir = D[i];
                if (cat_dir.EndsWith("-")) {
                    continue;
                }

                string cat_name = cat_dir.Substring(base_dir.Length + 1);
                TestCatalog tCat =
                    new TestCatalog() { name = Path.GetFileNameWithoutExtension(D[i]), Cases = new List<TestCase>() };
                string[] F = Directory.GetFiles(cat_dir, "*.tc");
                for (int j = 0, len_j = F.Length; j < len_j; j++) {
                    string case_file = F[j];
                    string case_name = System.IO.Path.GetFileNameWithoutExtension(case_file);
                    int ext_len = System.IO.Path.GetExtension(case_file).Length;

                    tCat.Cases.Add(new TestCase() {
                        name = case_name, case_file = case_file,
                        config_file = case_file.Substring(0, case_file.Length - ext_len) + "/default.config"
                    });
                }
                catalogs.Add(tCat);
            }

            return catalogs;
        }

        public static void load_case(NodeConfig nConfig, NodeConfig runConfig, bool reserv_uri = false) {
            string case_name = nConfig.tc;
            string case_file = 
                base_dir + "/" + case_name + ".tc";
            string config_file = 
                base_dir + "/" + case_name + "/default.config";
            //
            MyMainConfig myConfig = XmlHelper.Parse<MyMainConfig>(case_file);

            nConfig.trig_on_changed = false;
            try {
                if (reserv_uri) {
                    myConfig.uri = nConfig.uri;
                }
                myConfig.get_values(nConfig, true);
            } finally {
                nConfig.trig_on_changed = true;
            }

            for (int i = 0, len = myConfig.Flow.Count; i < len; i++) {
                MyNodeConfig myConfig_i = myConfig.Flow[i];
                NodeConfig runConfig_i = runConfig.get(myConfig_i.uri);
                try {
                    if (runConfig_i == null) {
                        throw new Exception("节点未找到：" + myConfig_i.uri);
                    } else {
                        runConfig_i = runConfig_i.clone();
                    }

                    runConfig_i.trig_on_changed = false;
                    
                    myConfig_i.get_values(runConfig_i);

                    //添加参数列表
                    if (runConfig_i.configs != null && runConfig_i.configs.Count > 0) {
                        for (int j = 0, len_j = runConfig_i.configs.Count; j < len_j; j++) {
                            NodeConfig config_j = runConfig_i.configs[j];
                            config_j.Parent = runConfig_i;

                            if (string.IsNullOrEmpty(config_j.param)) {
                                continue;
                            }
                            if (nConfig.configs == null) {
                                nConfig.configs = new NodeConfig();
                            }
                            nConfig.configs.Add(config_j);
                        }
                    }

                    nConfig.Add(runConfig_i);
                } finally {
                    if (runConfig_i != null) {
                        runConfig_i.trig_on_changed = true;
                    }
                }
            }

            //Call<object[], LuaContext, NodeConfig> OnInit = null;
            //Call<object[], NodeConfig> OnSet = null;
            //for (int i = 0, len = myr.Count; i < len; i++) {
            //    NodeConfig config_i = myr[i].Exec;
            //    this.runConfig.Add(config_i);

            //    if (config_i.OnInit != null) {
            //        if (OnInit == null) {
            //            OnInit = config_i.OnInit;
            //        } else {
            //            OnInit += config_i.OnInit;
            //        }
            //    }
            //    if (config_i.OnSet != null) {
            //        if (OnSet == null) {
            //            OnSet = config_i.OnSet;
            //        } else {
            //            OnSet += config_i.OnSet;
            //        }
            //    }

            //    //添加参数列表
            //    if (config_i.configs != null && config_i.configs.Count > 0) {
            //        for (int j = 0, len_j = config_i.configs.Count; j < len_j; j++) {
            //            NodeConfig config_j = config_i.configs[j];
            //            config_j.Parent = config_i;

            //            if (string.IsNullOrEmpty(config_j.param)) {
            //                continue;
            //            }
            //            this.runConfig.configs.Add(config_j);
            //        }
            //    }
            //}

            //if (this.runConfig.configs != null && this.runConfig.configs.Count > 0) {
            //    foreach (NodeConfig rConfig in this.runConfig.configs) {
            //        rConfig.OnSet = OnSet;
            //    }
            //}

            //if (OnInit != null) {
            //    OnInit(this.luaContext, this.runConfig);
            //}

            //加载配置项
            ConfigManager.load_config(config_file, nConfig);
        }
    }

    public class TestCase {
        public string name { get; set; }

        public string case_file { get; set; }

        public string config_file { get; set; }

        public string version { private get; set; }

        public override string ToString() {
            return this.version;
        }

        private MyMainConfig config;

        public MyMainConfig load(ConfigManager configManager,
            LuaContext luaContext, NodeConfig nConfig, NodeConfig runConfig, MySelector appFlow) {
            //
            MyMainConfig myConfig = XmlHelper.Parse<MyMainConfig>(this.case_file);

            runConfig.trig_on_changed = false;
            try {
                myConfig.get_values(runConfig, true);
                
            } finally {
                runConfig.trig_on_changed = true;
            }

            for (int i = 0, len = myConfig.Flow.Count; i < len; i++) {
                MyNodeConfig myConfig_i = myConfig.Flow[i];
                NodeConfig runConfig_i = nConfig.get(myConfig_i.uri);
                try {
                    if (runConfig_i == null) {
                        throw new Exception("节点未找到：" + myConfig_i.uri);
                    } else {
                        runConfig_i = runConfig_i.clone();
                    }

                    runConfig_i.trig_on_changed = false;
                    //加载程序值
                    myConfig_i.get_values(runConfig_i);
                    //加载配置值
                    configManager.setConfig(runConfig_i, true);
                    //加载程序节点
                    appFlow.Add(runConfig_i, myConfig_i.x, myConfig_i.y, myConfig_i.name);
                } finally {
                    if (runConfig_i != null) {
                        runConfig_i.trig_on_changed = true;
                    }
                }
            }
            
            //加载程序图连接线
            for (int i = 0, len = myConfig.Flow.Count; i < len; i++) {
                MyNodeConfig myConfig_i = myConfig.Flow[i];
                MyRender myR = appFlow.get_byname(myConfig_i.name);
                if (myR == null) {
                    continue;
                }

                if (myConfig_i.uris != null && myConfig_i.uris.Count > 0) {
                    for (int j = 0, len_j = myConfig_i.uris.Count; j < len_j; j++) {
                        MyRender myr_c = appFlow.get_byname(myConfig_i.uris[j]);
                        if (myr_c == null) {
                            continue;
                        }

                        appFlow.add_connect(myR.render, myr_c.render);
                    }
                }
            }

            this.config = myConfig;
            this.version = string.Format("{0}[{1}]", Path.GetFileNameWithoutExtension(this.case_file), myConfig.version);
            return this.config;
        }

        public void save(NodeConfig runConfig, List<MyRender> renders) {
            MyMainConfig myConfig = new MyMainConfig() {
                version = DateTime.Now.ToString("yyyyMMdd-HHmmss"), Flow = new List<MyNodeConfig>()
            };
            myConfig.set_values(runConfig, 0, 0, true);

            for (int i = 0, len = renders.Count; i < len; i++) {
                MyRender myr_i = renders[i];
                //节点
                NodeConfig nConfig_i = myr_i.Exec;
                MyNodeConfig mconfig_i = new MyNodeConfig() { name = myr_i.Name };
                mconfig_i.set_values(nConfig_i, myr_i.X, myr_i.Y);
                myConfig.Flow.Add(mconfig_i);

                //子节点
                if (myr_i.N == null || myr_i.N.Count == 0) {
                    continue;
                } else {
                    mconfig_i.uris = new List<string>();
                    for (int j = 0, len_j = myr_i.N.Count - 1; j <= len_j; j++) {
                        mconfig_i.uris.Add(myr_i.N[j].Name);
                    }
                }
            }
            //
            XmlHelper.ToString<MyMainConfig>(myConfig, this.case_file);
        }
    }
    #endregion

    #region save and load

    public class MyMainConfig : MyNodeConfig {
        [XmlAttribute]
        public string version { get; set; }
        [XmlAttribute]
        public string author { get; set; }
        [XmlAttribute]
        public DateTime create { get; set; }
        [XmlAttribute]
        public DateTime update { get; set; }
        [XmlAttribute]
        public string remark { get; set; }

        [XmlElement]
        public List<MyNodeConfig> Flow { get; set; }

        public void save(string file) {
            BinHelper<MyMainConfig>.ToString(this, file);
        }

        public void load(string file) {
            BinHelper<MyMainConfig>.Parse(file, this);
        }

        //public static implicit operator NodeConfig(MyMainConfig config) {
        //    NodeConfig nConfig = null;

        //    return nConfig;
        //}
    }

    public class MyNodeConfig {
        [XmlAttribute]
        public string name { get; set; }
        [XmlAttribute]
        public string alias { get; set; }
        [XmlAttribute]
        public string uri { get; set; }
        [XmlAttribute]
        public string key_config { get; set; }

        [XmlAttribute]
        public bool disable { get; set; }
        [XmlAttribute]
        public bool pause { get; set; }
        [XmlAttribute]
        public bool show_node { get; set; }
        [XmlElement]
        public string pause_message { get; set; }
        [XmlAttribute]
        public bool pause_after { get; set; }
        [XmlAttribute]
        public bool show_node_after { get; set; }
        [XmlElement]
        public string pause_after_message { get; set; }
        [XmlAttribute]
        public bool is_init { get; set; }
        [XmlAttribute]
        public bool is_first { get; set; }
        [XmlAttribute]
        public bool is_final { get; set; }
        [XmlAttribute]
        public bool is_editable { get; set; }

        [XmlAttribute]
        public bool log { get; set; }

        [XmlAttribute]
        public int times { get; set; }
        [XmlAttribute]
        public int step { get; set; }
        [XmlAttribute]
        public double delay { get; set; }

        [XmlAttribute]
        public int tab { get; set; }

        [XmlAttribute]
        public bool w { get; set; }
        [XmlAttribute]
        public bool c { get; set; }
        [XmlAttribute]
        public bool r { get; set; }
        [XmlAttribute]
        public bool v { get; set; }

        [XmlAttribute]
        public bool data { get; set; }
        [XmlElement]
        public string data_tag { get; set; }
        [XmlElement]
        public string data_spec { get; set; }
        [XmlAttribute]
        public bool param_en { get; set; }
        [XmlElement]
        public string param_file { get; set; }
        [XmlAttribute]
        public bool result_en { get; set; }
        [XmlElement]
        public string result_file { get; set; }

        [XmlElement]
        public string formula_before { get; set; }
        [XmlElement]
        public string formula_after { get; set; }

        [XmlAttribute]
        public double x { get; set; }
        [XmlAttribute]
        public double y { get; set; }

        [XmlArray]
        public List<string> uris { get; set; }

        [XmlArray]
        public List<MyConfigItem> Items { get; set; }

        public void set_values(NodeConfig nConfig, double x, double y, bool ignore_items = false) {
            if (nConfig == null) {
                return;
            }
            
            this.alias = nConfig.alias; this.uri = nConfig.uri;
            this.key_config = nConfig.key_config;

            this.disable = nConfig.disable; this.is_init = nConfig.is_init; this.is_first = nConfig.is_first; this.is_final = nConfig.is_final; this.is_editable = nConfig.is_editable;
            this.pause = nConfig.pause; this.show_node = nConfig.show_node; this.pause_message = nConfig.pause_message;
            this.pause_after = nConfig.pause_after; this.show_node_after = nConfig.show_node_after; this.pause_after_message = nConfig.pause_after_message;
            this.log = nConfig.log;

            this.times = nConfig.times; this.delay = nConfig.delay;

            this.tab = nConfig.tab;
            this.w = nConfig.w; this.c = nConfig.c; this.r = nConfig.r; this.v = nConfig.v;

            this.data = nConfig.data; 
            this.data_tag = nConfig.data_tag;
            this.data_spec = nConfig.data_spec;
            this.param_en = nConfig.param_en;
            this.param_file = nConfig.param_file;
            this.result_en = nConfig.result_en;
            this.result_file = nConfig.result_file;

            this.formula_before = nConfig.formula_before;
            this.formula_after = nConfig.formula_after;

            this.x = x; this.y = y;            

            if (ignore_items) {
                return;
            }

            if (nConfig.configs == null || nConfig.configs.Count == 0) {
                this.Items = null; return;
            } else {
                NodeConfig configs = nConfig.configs;
                this.Items = new List<MyConfigItem>();
                for (int i = 0, len = configs.Count; i < len; i++) {
                    NodeConfig config_i = configs[i];
                    this.Items.Add(new MyConfigItem() {
                        name = config_i.name, param = config_i.param, key_config = config_i.key_config,

                        w = config_i.w, c = config_i.c, r = config_i.r, v = config_i.v,

                        s = config_i.s,

                        p = config_i.p,
                        R = config_i.R,
                        data = config_i.data,

                        formula = config_i.formula,
                        remark = config_i.remark
                    });
                }
            }
        }

        /// <summary>
        /// 加载节点测试用例设置
        /// </summary>
        /// <param name="nConfig"></param>
        /// <param name="ignore_items"></param>
        public void get_values(NodeConfig nConfig, bool ignore_items = false) {
            if (nConfig == null) {
                return;
            }
            nConfig.name = this.name;
            nConfig.alias = this.alias; nConfig.uri = this.uri;
            nConfig.key_config = this.key_config;

            nConfig.disable = this.disable; nConfig.is_init = this.is_init; nConfig.is_first = this.is_first; nConfig.is_final = this.is_final; nConfig.is_editable = this.is_editable;
            nConfig.pause = this.pause; nConfig.show_node = this.show_node; nConfig.pause_message = this.pause_message;
            nConfig.pause_after = this.pause_after; nConfig.show_node_after = this.show_node_after; nConfig.pause_after_message = this.pause_after_message;
            nConfig.log = this.log;

            nConfig.times = this.times; nConfig.delay = this.delay;

            nConfig.tab = this.tab;
            nConfig.w = this.w; nConfig.c = this.c; nConfig.r = this.r; nConfig.v = this.v;

            nConfig.data = this.data; 
            nConfig.data_tag = this.data_tag;
            nConfig.data_spec = this.data_spec;
            nConfig.param_en = this.param_en;
            nConfig.param_file = this.param_file;
            nConfig.result_en = this.result_en;
            nConfig.result_file = this.result_file;

            nConfig.formula_before = this.formula_before;
            nConfig.formula_after = this.formula_after;
            

            if (this.Items == null || this.Items.Count == 0) {
                return;
            }
            if (nConfig.configs == null || nConfig.configs.Count == 0) {
                return;
            }

            if (ignore_items) {
                return;
            }

            for (int i = 0, len_i = this.Items.Count; i < len_i; i++) {
                MyConfigItem myc_i = this.Items[i];
                for (int j = 0, len_j = nConfig.configs.Count; j < len_j; j++) {
                    NodeConfig nConfig_j = nConfig.configs[j];
                    nConfig_j.trig_on_changed = false;
                    try {
                        if (nConfig_j.name == myc_i.name) {
                            nConfig_j.param = myc_i.param;
                            nConfig_j.key_config = myc_i.key_config;

                            nConfig_j.w = myc_i.w;
                            nConfig_j.c = myc_i.c;
                            nConfig_j.r = myc_i.r;
                            nConfig_j.v = myc_i.v;

                            nConfig_j.s = myc_i.s;

                            nConfig_j.data = myc_i.data;
                            nConfig_j.p = myc_i.p;
                            nConfig_j.R = myc_i.R;

                            nConfig_j.formula = myc_i.formula;
                            nConfig_j.remark = myc_i.remark;
                            break;
                        }
                    } finally {
                        nConfig_j.trig_on_changed = true;
                    }
                }
            }
            //
            for (int i = 0, len = nConfig.configs.Count; i < len; i++) {
                NodeConfig nConfig_i = nConfig.configs[i];
                if (string.IsNullOrEmpty(nConfig_i.key_config)) {
                    string myr_hash =
                    "@" + Crypto.MD5(nConfig_i.GetHashCode().ToString() + "-" + Guid.NewGuid().ToString()).ToUpper();
                    nConfig_i.key_config = nConfig_i.uri + myr_hash;
                }
            }
        }
    }

    public class MyConfigItem {
        [XmlAttribute]
        public string name { get; set; }
        [XmlAttribute]
        public string param { get; set; }
        [XmlAttribute]
        public string key_config { get; set; }

        [XmlAttribute]
        public bool w { get; set; }
        [XmlAttribute]
        public bool c { get; set; }
        [XmlAttribute]
        public bool r { get; set; }
        [XmlAttribute]
        public bool v { get; set; }

        [XmlAttribute]
        public bool s { get; set; }

        [XmlAttribute]
        public bool data { get; set; }

        [XmlAttribute]
        public bool p { get; set; }

        [XmlAttribute]
        public bool R { get; set; }


        [XmlElement]
        public string formula { get; set; }

        [XmlElement]
        public string remark { get; set; }
    }

    #endregion
}
